	package com.xiaobu.go;

import java.util.Calendar;

import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

public class StepService extends Service
{
	private static final String TAG = "com.xiaobu.go.StepService";
	private StepPreferences mSettings;
    private SensorManager mSensorManager;
    private Sensor mSensor;
    private StepDetector mStepDetector;
    
    private StepCounter mStepCounter;
    
    private PowerManager.WakeLock mWakeLock;
    private NotificationManager mNM;
    
    private long prevSavedCount;
    private long prevTodayStartCount;
    
    private int prevLastSavedDate;
    private int prevLastSavedTime;
    
    private long todayStartCount = 0L;
    
    private int lastSavedDate = 0;
    private int lastSavedTime = 0;
    
    private StepTimer timer;
    private StepTimer.TimerInterface stepTimer;
    private StepTimer.TimerInterface saveTimer;
    
    public class StepBinder extends Binder
    {
        StepService getService() {
            return StepService.this;
        }
    }
    
    @Override
    public void onCreate()
    {
    	Log.i(TAG, "[SERVICE] onCreate");
        super.onCreate();
        
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        
        mSettings = new StepPreferences(PreferenceManager.getDefaultSharedPreferences(this));

        acquireWakeLock();
        
        mStepCounter = new StepCounter(mSettings);
        mStepCounter.addStepListener(mStepListener);
        
        // Start detecting
        mStepDetector = new StepDetector(this);
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        registerDetector();
        
        restorePreferences();
        saveTodaySteps();
        
        this.timer = new StepTimer();
        
        this.stepTimer = this.timer.addMyTimerListener(new Runnable() {
        	public void run() {
        		StepService.this.saveTodaySteps();
        	}
        } , true, 60);
        
        this.stepTimer.enable();
        
        this.saveTimer = this.timer.addMyTimerListener(new Runnable() {
        	public void run() {
        		StepService.this.savePreferences();
        	}
        }, true, 600);
        
        this.saveTimer.enable();

        // Register our receiver for the ACTION_SCREEN_OFF action. This will make our receiver
        // code be called whenever the phone enters standby mode.
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        registerReceiver(mReceiver, filter);

        // Tell the user we started.
        Toast.makeText(this, getText(R.string.started), Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onStart(Intent intent, int startId)
    {
    	Log.i(TAG, "[SERVICE] onStart");
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy()
    {
    	Log.i(TAG, "[SERVICE] onDestroy");
    	
    	if (this.timer != null)
    	{
    		this.timer.stop();
    	}
    	
    	savePreferences();
    	
        unregisterReceiver(mReceiver);
        unregisterDetector();
        
        mNM.cancel(R.string.app_name);

        mWakeLock.release();
        
        super.onDestroy();
        
        // Stop detecting
        mSensorManager.unregisterListener(mStepDetector);

        // Tell the user we stopped.
        Toast.makeText(this, getText(R.string.stopped), Toast.LENGTH_SHORT).show();
    }

    private void registerDetector()
    {
        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(mStepDetector, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
    }

    private void unregisterDetector()
    {
        mSensorManager.unregisterListener(mStepDetector);
    }

    @Override
    public IBinder onBind(Intent intent)
    {
    	Log.i(TAG, "[SERVICE] onBind");
        return mBinder;
    }
    
    private final IBinder mBinder = new StepBinder();

    public interface ICallback {
        public void stepsChanged(int value);
    }
    
    private ICallback mCallback;

    public void registerCallback(ICallback cb)
    {
        mCallback = cb;
    }
    
    public void displaySteps()
    {
    	mStepListener.stepsChanged((int)mStepCounter.getSteps());
    }
    
    public void resetValues()
    {
    	mStepCounter.setSteps(0);
    }
    
    public long getSteps()
    {
    	return mStepCounter.getSteps();
    }
    
    private StepListener mStepListener = new StepListener() {
    	public void onStep()
    	{
    		stepsChanged((int)mStepCounter.getSteps());
    	}
        public void stepsChanged(int steps) {
        	if (mCallback != null) {
                mCallback.stepsChanged(steps);
            }
        }
    };

    // BroadcastReceiver for handling ACTION_SCREEN_OFF.
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Check action just to be on the safe side.
            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                // Unregisters the listener and registers it again.
                StepService.this.unregisterDetector();
                StepService.this.registerDetector();
                if (mSettings.wakeAggressively()) {
                	mWakeLock.release();
                    acquireWakeLock();
                }
            }
        }
    };

    private void acquireWakeLock()
    {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        int wakeFlags;
        if (mSettings.wakeAggressively())
        {
            wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
        }
        else if (mSettings.keepScreenOn())
        {
            wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK;
        }
        else
        {
            wakeFlags = PowerManager.PARTIAL_WAKE_LOCK;
        }
        mWakeLock = pm.newWakeLock(wakeFlags, TAG);
        mWakeLock.acquire();
    }
    
    public void action(SensorEvent event)
    {
    	mStepCounter.action(event);
    }
    
    private void restorePreferences()
	{
    	long totalStepCount = getSharedPreferences("PersistentData", Context.MODE_PRIVATE).getLong("TotalStepCount", 0L);
    	mStepCounter.setTotalStepCount(totalStepCount);
    	this.prevSavedCount = totalStepCount;
    	
    	long todayStartCount = getSharedPreferences("PersistentData", Context.MODE_PRIVATE).getLong("TodaysStartCount", 0L);
    	setTodayStartCount(todayStartCount);
    	this.prevTodayStartCount = todayStartCount;
    	
    	int lastSavedDate = getSharedPreferences("PersistentData", Context.MODE_PRIVATE).getInt("LastSavedDate", 0);
    	setLastSavedDate(lastSavedDate);
    	this.prevLastSavedDate = lastSavedDate;
    	
    	int lastSavedTime = getSharedPreferences("PersistentData", Context.MODE_PRIVATE).getInt("LastSavedTime", 0);
    	setLastSavedTime(lastSavedTime);
    	this.prevLastSavedTime = lastSavedTime;
	}
	
	private void savePreferences()
	{
		boolean update = false;
		
		long totalSteps = mStepCounter.getTotalStepCount();
		long todaySteps = getTodayStartCount();
		
		int lastSavedDate = getLastSavedDate();
		int lastSavedTime = getLastSavedTime();
		
		SharedPreferences.Editor editor = getSharedPreferences("PersistentData", 0).edit();
		
		if (totalSteps != this.prevSavedCount)
	    {
			editor.putLong("TotalStepCount", totalSteps);
			this.prevSavedCount = totalSteps;
			update = true;
	    }
		
		if (todaySteps != this.prevTodayStartCount)
	    {
			editor.putLong("TodaysStartCount", totalSteps);
			this.prevTodayStartCount = todaySteps;
			update = true;
	    }
		
		if (lastSavedDate != this.prevLastSavedDate)
	    {
			editor.putInt("LastSavedDate", lastSavedDate);
			this.prevLastSavedDate = lastSavedDate;
			update = true;
	    }
		
		if (lastSavedTime != this.prevLastSavedTime)
	    {
			editor.putInt("LastSavedTime", lastSavedTime);
			this.prevLastSavedTime = lastSavedTime;
			update = true;
	    }
		
		if (update)
		{
			editor.commit();
		}
	}
	
	private long getTodayStartCount()
	{
	    return this.todayStartCount;
	}
	
	private void setTodayStartCount(long todaySteps)
	{
	    this.todayStartCount = todaySteps;
	}
	
	private int getLastSavedDate()
	{
	    return this.lastSavedDate;
	}
	
	private void setLastSavedDate(int savedDate)
	{
	    this.lastSavedDate = savedDate;
	}
	
	private int getLastSavedTime()
	{
		return this.lastSavedTime;
	}
	
	private void setLastSavedTime(int savedTime)
	{
		this.lastSavedTime = savedTime;
	}
	
	private void saveTodaySteps()
    {
		Calendar calendar = Calendar.getInstance();
		int today = 100000 * calendar.get(Calendar.YEAR)
				+ 1000 * calendar.get(Calendar.MONTH)
				+ 10 * calendar.get(Calendar.DATE)
				+ 1 * calendar.get(Calendar.DAY_OF_WEEK);
		
		int time = 100 * calendar.get(Calendar.HOUR_OF_DAY)
				+ 1 * calendar.get(Calendar.MINUTE);
		
		if (getLastSavedDate() == 0)
		{
			setLastSavedDate(today);
		}
		
		if (getLastSavedTime() == 0)
		{
			setLastSavedTime(time);
		}
		
		if (getLastSavedDate() != today)
		{
			int savedDate = getLastSavedDate();
		    int year = savedDate / 100000;
		    int year_off = savedDate % 100000;
		    int month = 1 + year_off / 1000;
		    int date = year_off % 1000 / 10;
		    
		    int savedTime = getLastSavedTime();
		    int hour = savedTime / 100;
		    int minute = savedTime % 100;
		    
		    setLastSavedDate(today);
		    setLastSavedTime(time);
		    
		    int steps = (int)(mStepCounter.getTotalStepCount() - getTodayStartCount());
		    
		    StepDatabase stepDatabase = new StepDatabase(this);
		    stepDatabase.addData(new StepDatabase.LogData(-1, year, month, date, hour, minute, steps, 0));
		    stepDatabase.close();
		    
		    setTodayStartCount(mStepCounter.getTotalStepCount());
		    savePreferences();
		}
    }
}